package com.citywithincity.db;

import java.sql.Connection;
import java.sql.SQLException;

import com.citywithincity.api.ApiConfig;
import com.citywithincity.db.interfaces.Ar;
import com.citywithincity.db.interfaces.EAr;
import com.citywithincity.db.interfaces.IDataSourceProvider;
import com.citywithincity.db.interfaces.ITrans;
import com.citywithincity.db.interfaces.SqlDriver;
import com.citywithincity.db.meta.DbStructFactory;


@SuppressWarnings("unchecked")
public class Db {

	private static ThreadLocal<ArFactory>[] threadLocal;
	
	private static IDataSourceProvider[] extraDataSourceProviders;

	private static ConnectionDescription[] connectionDescriptions;
	
	/**
	 * 关闭
	 */
	public static void close(){
		for(int i=0 , count = extraDataSourceProviders.length; i < count; ++i){
			if(extraDataSourceProviders[i]!=null){
				extraDataSourceProviders[i].close();
			}
		}
	}

	public static IDataSourceProvider getDataSourceProvider(int index){
		return extraDataSourceProviders[index];
	}

	/**
	 * 这里下标从0开始
	 * @param index
	 * @param dataSourceProvider
	 */
	public static void create(ConnectionDescription[] cds){
		connectionDescriptions = cds;
		extraDataSourceProviders = new IDataSourceProvider[cds.length];
		threadLocal = new ThreadLocal[cds.length];
		for (int i = 0; i < cds.length; ++i) {
			threadLocal[i] = new ThreadLocal<ArFactory>();
		}
		int index = 0;
		for (ConnectionDescription connectionDescription : cds) {
			connectionDescription.getStruct().setDb(index);
			IDataSourceProvider dataSourceProvider = createDataSourceProvider(connectionDescription);
			extraDataSourceProviders[index++] = dataSourceProvider;
		}
	}
	
	
	
	
	/**
	 * 默认情况下，使用阿里巴巴的连接池s
	 * @param connectionDescription
	 * @return
	 */
	private static IDataSourceProvider createDataSourceProvider(ConnectionDescription connectionDescription) {
		
		return ApiConfig.me().createDataSourceProvider(connectionDescription);
		
	}

	/**
	 * 获取连接
	 * 
	 * @return
	 * @throws SQLException
	 */
	public static ArFactory getDbDescriptor() {
		return getDbDescriptor(0);
	}
	
	public static void release(){
		if(threadLocal!=null){
			for (ThreadLocal<ArFactory> iDataSourceProvider : threadLocal) {
				ArFactory descriptor = iDataSourceProvider.get();
				if(descriptor!=null){
					descriptor.releaseConnection();
				}
			}
		}
		
	}
	
	
	
	public static ArFactory getDbDescriptor(int index){
		ArFactory descriptor =threadLocal[index].get();
		if(descriptor==null){
			// 创建,默认的链接
			try {
				descriptor = extraDataSourceProviders[index].createArFactory();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			threadLocal[index].set(descriptor);
		}
		return descriptor;
	}
	
	/**
	 * 常用查询
	 * @param index
	 * @return
	 */
	public static Ar ar(int index){
		return getDbDescriptor(index).createAr();
	}
	
	/**
	 * 和sql驱动相关
	 * @param index
	 * @return
	 */
	public static SqlDriver getDriver(int index){
		return connectionDescriptions[index].getSqlDriver();
	}
	
	/**
	 * 数据库结构
	 * @param db
	 * @return
	 */
	public static DbStructFactory getStruct(int db) {
		return connectionDescriptions[db].getStruct();
	}
	
	public static <T> EAr<T> ar(Class<T> clazz){
		Entity<T> info = EntityBuilder.getEntity(clazz);
		if (info == null)
			throw new RuntimeException("Class " + clazz + " not registered!");
		return getDbDescriptor(info.db).createEAr(info);
	}

	public static Ar ar(){
		return getDbDescriptor().createAr();
	}
	public static <Param,Result> Result trans(Transaction<Param,Result> transaction,Param param,int level){
		Connection connection = Db.ar().getConnection();
		int oldLevel = Connection.TRANSACTION_READ_COMMITTED;
		try {
			oldLevel = connection.getTransactionIsolation();
			if(level != oldLevel){
				connection.setTransactionIsolation(level);
			}
			connection.setAutoCommit(false);
			Result result = transaction.execute(param);
			connection.commit();
			return result;
		} catch (RuntimeException t) {
			try {
				connection.rollback();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			throw t;
		}catch (Throwable ex) {
			try {
				connection.rollback();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			throw new RuntimeException(ex);
		}finally {
			try{connection.setTransactionIsolation(oldLevel);}catch(Exception e){}
			try{connection.setAutoCommit(true);}catch(Exception e){
				throw new RuntimeException(e);
			}
		}
	}
	
	public static <Param,Result> Result trans(Transaction<Param,Result> transaction,Param param){
		Connection connection = Db.ar().getConnection();
		try {
			connection.setAutoCommit(false);
			Result result = transaction.execute(param);
			connection.commit();
			return result;
		} catch (RuntimeException t) {
			try {
				connection.rollback();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			throw t;
		}catch (Throwable ex) {
			try {
				connection.rollback();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			throw new RuntimeException(ex);
		}finally {
			try{connection.setAutoCommit(true);}catch(Exception e){
				throw new RuntimeException(e);
			}
		}
	}
	public static boolean transaction(ITrans trans){
		return transaction(Connection.TRANSACTION_READ_COMMITTED, trans);
	}

	/**
	 * 任务
	 * @param trans
	 * @param args
	 */
	public static boolean transaction(int transLevel,ITrans trans){
		Connection connection = Db.ar().getConnection();
		Integer oldLevel = null;
		try {
			oldLevel = connection.getTransactionIsolation();
			if(oldLevel!=transLevel){
				connection.setTransactionIsolation(transLevel);
			}
			connection.setAutoCommit(false);
			if(trans.doTrans()){
				connection.commit();
				return true;
			}else{
				connection.rollback();
			}
		} catch (RuntimeException t) {
			try {
				connection.rollback();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			throw t;
		}catch (Throwable ex) {
			try {
				connection.rollback();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			throw new RuntimeException(ex);
		}finally {
			try{
				if(oldLevel!=null && oldLevel!=transLevel){
					try {
						connection.setTransactionIsolation(oldLevel);
					} catch (Exception e) {
						throw new RuntimeException(e);
					} finally {
						try{
							connection.setAutoCommit(true);
						}catch(Exception e){
							throw new RuntimeException(e);
						}
					}
				}else{
					try{
						connection.setAutoCommit(true);
					}catch(Exception e){
						throw new RuntimeException(e);
					}
				}
				
			}finally{
				Db.release();
			}
			
			
		}
		return false;
	}

	

}
